package com.lnj.orders.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lnj.bean.model.Coupon;
import com.lnj.orders.mapper.CouponMapper;
import com.lnj.orders.service.CouponService;
import com.lnj.bean.vo.CouponMessage;
import com.lnj.orders.utils.JsonUtil;
import com.lnj.utils.RedisKeys;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class CouponServiceImpl implements CouponService {
    private static final String LUA_SCRIPT_PATH = "lua/coupon.lua"; // Lua 脚本路径

    @Autowired
    private CouponMapper couponMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private JsonUtil jsonUtil;

    @Autowired
    private KafkaTemplate kafkaTemplate;

    @Autowired
    private RedissonClient redissonClient; // 用于分布式锁

    private DefaultRedisScript<Long> redisScript;// 用于执行 Lua 脚本

    //初始化Lua脚本
    @PostConstruct
    public void initLuaScript() {
        redisScript = new DefaultRedisScript<>();
        redisScript.setLocation(new ClassPathResource(LUA_SCRIPT_PATH));//加载Lua脚本
        redisScript.setResultType(Long.class);//设置Lua脚本的返回值类型
    }

    @Override
    public List<Coupon> getAllCouponList() {
        QueryWrapper<Coupon> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("status", 1);
        return couponMapper.selectList(queryWrapper);
    }

    /**
     * 用户领取优惠券
     * @param userId 用户ID
     * @param couponId 优惠券ID
     * @param limit 每人限制领取当前优惠券数量
     */
    @Override
    public Integer receiveCoupon(Long userId, Long couponId,Integer limit) {
        String couponKey = RedisKeys.COUPON_COUNT + couponId;//优惠券数量
        String userCouponKey = RedisKeys.COUPON_USER_COUNT + userId + ":" + couponId;//用户领取优惠券的数量
        //执行Lua脚本
        Long result = stringRedisTemplate.execute(redisScript,
                List.of(couponKey, userCouponKey),
                userId.toString(), couponId.toString(),limit.toString());

        if (result==null){
            log.error("优惠券领取失败,优惠券ID:{},用户ID:{}",couponId,userId);
            return null;
        }
        if (result==2){
            sendCouponMessage(userId,couponId);
            log.info("优惠券消息发送成功,优惠券ID:{},用户ID:{}",couponId,userId);
            return 2;
        }

        return result.intValue();
    }

    /**
     * 发送Kafka消息
     */
    private void sendCouponMessage(Long userId, Long couponId) {
        CouponMessage couponMessage = new CouponMessage();
        couponMessage.setCouponId(couponId);
        couponMessage.setUserId(userId);
        couponMessage.setCreateTime(LocalDateTime.now());
        try {
            kafkaTemplate.send("coupon",jsonUtil.toJson(couponMessage));
            log.info("优惠券消息发送成功,优惠券消息:{}",couponMessage);
        } catch (Exception e) {
            log.error("优惠券消息发送失败,优惠券消息:{}",couponMessage,e);
            // 失败补偿：回滚Redis库存
            rollbackStock(userId, couponId);
        }
    }

    /**
     * 回滚库存
     */
    private void rollbackStock(Long userId, Long couponId) {
        //// 使用优惠券ID作为锁key，保证同一优惠券的回滚串行化
        RLock lock = redissonClient.getLock("seckill:rollback:" + couponId);
        try {
            //尝试获取锁，等待三秒，获取10秒锁自动释放
            if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
                String couponKey = RedisKeys.COUPON_COUNT + couponId;//优惠券数量
                String userCouponKey = RedisKeys.COUPON_USER_COUNT + userId + ":" + couponId;//用户领取优惠券的数量

                stringRedisTemplate.opsForValue().increment(couponKey);
                Long count = stringRedisTemplate.opsForValue().decrement(userCouponKey);
                if (count != null && count <= 0) {
                    stringRedisTemplate.delete(userCouponKey);
                }
                log.info("优惠券回滚成功,优惠券ID:{},用户ID{}",couponId,userId);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("回滚操作被中断", e);
        } finally {
            lock.unlock();
        }
    }
}
